/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.aspectj.annotation;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aspectj.lang.reflect.PerClauseKind;

import org.springframework.aop.Advisor;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * Helper for retrieving @AspectJ beans from a BeanFactory and building
 * Spring Advisors based on them, for use with auto-proxying.
 *
 * @author Juergen Hoeller
 * @since 2.0.2
 * @see AnnotationAwareAspectJAutoProxyCreator
 */
/*
1. 总结
BeanFactoryAspectJAdvisorsBuilder 是 Spring AOP 中用于从 BeanFactory 中检索带有 @AspectJ 注解的切面 Bean，
并基于它们构建 Spring Advisors 的辅助类。
该类支持单例和原型模式的切面实例创建，并对切面进行缓存以提高性能。
主要被 AnnotationAwareAspectJAutoProxyCreator 使用，用于自动代理创建过程中识别和应用切面。
2. 内部成员说明
	beanFactory: ListableBeanFactory 类型，用于扫描和获取切面 Bean。
	advisorFactory: AspectJAdvisorFactory 类型，用于创建每个 Advisor。
	aspectBeanNames: List<String> 类型，缓存已识别的切面 Bean 名称。
	advisorsCache: Map<String, List<Advisor>> 类型，缓存单例切面对应的 Advisors。
	aspectFactoryCache: Map<String, MetadataAwareAspectInstanceFactory> 类型，缓存原型切面对应的工厂实例。
方法说明
	buildAspectJAdvisors(): 查找 BeanFactory 中所有带有 @AspectJ 注解的切面 Bean，并返回表示它们的 Spring AOP Advisors 列表。
	isEligibleBean(String beanName): 判断给定名称的切面 Bean 是否符合条件，默认实现返回 true。
3. 类关系结构
相关类和接口关系
与 AspectJAdvisorFactory 接口：BeanFactoryAspectJAdvisorsBuilder 使用 AspectJAdvisorFactory 实例（默认为 ReflectiveAspectJAdvisorFactory）来创建 Advisors。
与 MetadataAwareAspectInstanceFactory 接口：用于创建切面实例，有两个主要实现：
BeanFactoryAspectInstanceFactory：用于单例切面
PrototypeAspectInstanceFactory：用于原型切面
4. 使用场景
在 Spring AOP 自动代理创建过程中，用于发现和处理 @AspectJ 注解的切面
当需要将 AspectJ 切面集成到 Spring AOP 代理机制中时
在 AnnotationAwareAspectJAutoProxyCreator 中作为核心组件使用
5. 实际代码应用示例
// 在 AnnotationAwareAspectJAutoProxyCreator 中的使用示例
public class AnnotationAwareAspectJAutoProxyCreator extends AbstractAutoProxyCreator {

    private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder;

    @Override
    protected Object[] getAdvicesAndAdvisorsForBean(
            Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

        // 使用 BeanFactoryAspectJAdvisorsBuilder 构建 Advisors
        List<Advisor> advisors = this.aspectJAdvisorsBuilder.buildAspectJAdvisors();

        // 根据 Advisors 和当前 Bean 确定是否需要代理
        // ...
        return null;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        if (!(beanFactory instanceof ListableBeanFactory)) {
            throw new IllegalArgumentException(
                    "AdvisorAutoProxyCreator requires a ListableBeanFactory: " + beanFactory);
        }
        // 初始化 BeanFactoryAspectJAdvisorsBuilder
        this.aspectJAdvisorsBuilder =
                new BeanFactoryAspectJAdvisorsBuilder((ListableBeanFactory) beanFactory);
    }
}


 */
public class BeanFactoryAspectJAdvisorsBuilder {

	private final ListableBeanFactory beanFactory;

	private final AspectJAdvisorFactory advisorFactory;

	@Nullable
	private volatile List<String> aspectBeanNames;

	private final Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<>();

	private final Map<String, MetadataAwareAspectInstanceFactory> aspectFactoryCache = new ConcurrentHashMap<>();


	/**
	 * Create a new BeanFactoryAspectJAdvisorsBuilder for the given BeanFactory.
	 * @param beanFactory the ListableBeanFactory to scan
	 */
	public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory) {
		this(beanFactory, new ReflectiveAspectJAdvisorFactory(beanFactory));
	}

	/**
	 * Create a new BeanFactoryAspectJAdvisorsBuilder for the given BeanFactory.
	 * @param beanFactory the ListableBeanFactory to scan
	 * @param advisorFactory the AspectJAdvisorFactory to build each Advisor with
	 */
	public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
		Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
		Assert.notNull(advisorFactory, "AspectJAdvisorFactory must not be null");
		this.beanFactory = beanFactory;
		this.advisorFactory = advisorFactory;
	}


	/**
	 * Look for AspectJ-annotated aspect beans in the current bean factory,
	 * and return to a list of Spring AOP Advisors representing them.
	 * <p>Creates a Spring Advisor for each AspectJ advice method.
	 * @return the list of {@link org.springframework.aop.Advisor} beans
	 * @see #isEligibleBean
	 *
	 * 本方法会被多次调用，因为一个Bean在判断要不要进行AOP时，都会调用这个方法
	 */
	public List<Advisor> buildAspectJAdvisors() {
		// aspectBeanNames是用来缓存BeanFactory中所存在的切面beanName的，第一次为null，后面就不为null了，不为null表示之前就已经找到过BeanFactory中的切面了
		List<String> aspectNames = this.aspectBeanNames;

		if (aspectNames == null) {
			synchronized (this) {
				aspectNames = this.aspectBeanNames;
				if (aspectNames == null) {
					List<Advisor> advisors = new ArrayList<>();
					aspectNames = new ArrayList<>();

					// 把所有beanNames拿出来遍历，判断某个bean的类型是否是Aspect
					String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
					for (String beanName : beanNames) {
						if (!isEligibleBean(beanName)) {
							continue;
						}
						// We must be careful not to instantiate beans eagerly as in this case they
						// would be cached by the Spring container but would not have been weaved.
						Class<?> beanType = this.beanFactory.getType(beanName, false);
						if (beanType == null) {
							continue;
						}
						if (this.advisorFactory.isAspect(beanType)) {
							aspectNames.add(beanName);
							// 切面的注解信息
							AspectMetadata amd = new AspectMetadata(beanType, beanName);

							// 如果@Aspect不是perthis、pertarget，那么一个切面只会生成一个对象（单例）
							// 并且会将该切面中所对应的Advisor对象进行缓存
							if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {

								MetadataAwareAspectInstanceFactory factory =
										new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
								// 利用BeanFactoryAspectInstanceFactory来解析Aspect类
								List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
								if (this.beanFactory.isSingleton(beanName)) {
									// 缓存切面所对应的所有Advisor对象
									this.advisorsCache.put(beanName, classAdvisors);
								}
								else {
									this.aspectFactoryCache.put(beanName, factory);
								}
								advisors.addAll(classAdvisors);
							}
							else {
								// Per target or per this.
								if (this.beanFactory.isSingleton(beanName)) {
									throw new IllegalArgumentException("Bean with name '" + beanName +
											"' is a singleton, but aspect instantiation model is not singleton");
								}
								MetadataAwareAspectInstanceFactory factory =
										new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
								this.aspectFactoryCache.put(beanName, factory);
								// 利用PrototypeAspectInstanceFactory来解析Aspect类
								// PrototypeAspectInstanceFactory的父类为BeanFactoryAspectInstanceFactory
								// 这两个Factory的区别在于PrototypeAspectInstanceFactory的构造方法中会判断切面Bean是不是原型，除此之外没有其他区别
								// 所以主要就是BeanFactoryAspectInstanceFactory来负责生成切面实例对象
								advisors.addAll(this.advisorFactory.getAdvisors(factory));
							}
						}
					}
					this.aspectBeanNames = aspectNames;
					return advisors;
				}
			}
		}

		if (aspectNames.isEmpty()) {
			return Collections.emptyList();
		}

		// 如果切面已经找到过了，那么则遍历每个切面是否缓存了对应的Advisor，如果没有缓存则进行解析得到Advisor
		List<Advisor> advisors = new ArrayList<>();
		for (String aspectName : aspectNames) {
			List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
			if (cachedAdvisors != null) {
				advisors.addAll(cachedAdvisors);
			}
			else {
				MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
				advisors.addAll(this.advisorFactory.getAdvisors(factory));
			}
		}
		return advisors;
	}

	/**
	 * Return whether the aspect bean with the given name is eligible.
	 * @param beanName the name of the aspect bean
	 * @return whether the bean is eligible
	 */
	protected boolean isEligibleBean(String beanName) {
		return true;
	}

}
